let W = null
//封装一个Watcher类
class Watcher {
  constructor(obj, key) {
    this.obj = obj
    this.key = key
    // 要获取值时,把当前的watcher保存在全局变量上
    W = this
    this.get()
    // 用完W就设置回去
    W = null
  }

  // 组件内new Watcher的时候就是想得到一个值,所以封装一个get方法
  get() {
    // 如果这样拿值,就一定会进到当前key响应式的getter函数中
    return this.obj[this.key]
  }

  // 更新时调用update方法
  update() {
    console.log("通知啦!请求数据" + this.get());
  }
}

//5.定义一个Dep类,用来收集依赖和通知更新
class Dep {
  constructor() {
    // 每次实例化Dep就会创建一个空的数组,然后用来保存所有的watcher
    this.subs = []
  }
  depend(watcher) {
    this.subs.push(watcher)
    console.log("每次访问我的数据的时候,就把watcher收集到dep.subs这个数组中", this.subs);
  }
  notify() {
    console.log("通知所有的watcher 重新加载新的数据");
    this.subs.forEach((watcher) => {
      watcher.update()
    })
  }
}

// 4.封装defineReactive对单个数据进行数据劫持(数据劫持的核心)
function defineReactive(_data, key, value) {

  // 把value值再次交给observe方法,进行深层次的数据响应
  observe(value)
  // 每次对一个值进行响应式的时候,实例化一个Dep类
  const dep = new Dep()
  Object.defineProperty(_data, key, {
    get() {
      console.log("正在获取" + key + "的值", "收集所有的依赖项watcher");
      // 当数据被请求的时候,把watcher收集到dep.subs中
      if (W) {
        dep.depend(W)
      }
      return value
    },
    set(newVal) {
      if (value === newVal) return
      console.log("正在设置" + key + "的值", "通知所有的watcher进行更新");
      value = newVal
      // 当响应式数据被修改的时候,要通知所有的dep.subs里的watcher进行更新
      dep.notify()
    }
  })
}

// 3.封装一个Observer类,用来进行发布订阅模式的核心操作(响应式操作:收集依赖,通知更新)
class Observer {
  // constructor接收要进行响应式处理的对象
  constructor(_data) {
    // 将_data放在实例上是为了能在任何原型方法上都能访问到这个数据(数据共享)
    this._data = _data
    // 判断当前的_data是对象还是数组
    if (Array.isArray(_data)) {
      this.observeArray(_data)
    } else {
      this.walk(_data)
    }
  }
  // 在Observer的原型对象上封装一个对数组操作的方法
  observeArray(_data) {
    _data.forEach((item) => {
      observe(item)
    })
  }
  // 在Observer的原型对象上封装一个对对象操作的方法
  walk(_data) {
    for (let key of Object.keys(_data)) {
      defineReactive(_data, key, _data[key])
    }
  }
}

// 2.封装一个observe函数,遍历this._data,将里面的值交给defineReactive进行数据劫持
function observe(_data) {
  if (typeof _data !== "object" || _data === null) return
  new Observer(_data)

}

// 1.封装一个Vue构造函数
function Vue(options) {
  // 将配置对象options中data的内容放在vm的_data中
  this._data = options.data
  // 开始数据代理,将_data中的数据拿出来放在vm身上
  for (let key of Object.keys(this._data)) {
    Object.defineProperty(this, key, {
      get() {
        return this._data[key]
      },
      set(newVal) {
        this._data[key] = newVal
      }
    })
  }
  // 拿到_data中的值进行数据劫持
  observe(this._data)
}

// 实例化Vue
const vm = new Vue({
  data: {
    count: 1,
    course: {
      type: "easy",
      name: "vue"
    }
  }
})

console.log(vm);
function huoqu() {
  new Watcher(vm, "count")
}

function shezhi() {
  vm.count = 3
}

huoqu()
shezhi()
